import {AxdrType} from "../asn1.axdr/AxdrType";
import {ReverseByteArrayInputStream} from "../ReverseByteArrayInputStream";
import {ReverseByteArrayOutputStream} from "../ReverseByteArrayOutputStream";
import {Buffer} from "buffer";
import {AxdrOctetString} from "../asn1.axdr/AxdrOctetString";
import {AxdrEnum} from "../asn1.axdr/AxdrEnum";
import {SID_MAC} from "./SID_MAC";
import {RN} from "./RN";
import {RN_MAC} from "./RN_MAC";
import {SID} from "./SID";


enum AppUnitChoice {
    _ERR_NONE_SELECTED = -1,
    APP_UNIT_PALIN = 0,
    APP_UNIT = 1,
}

export class SubChoiceAppUnit implements AxdrType{

    dataCode : Buffer | null = null;
    choice : AppUnitChoice = AppUnitChoice._ERR_NONE_SELECTED;
    appUnitPalin : AxdrOctetString | null = null;
    appUnit : AxdrOctetString | null = null;

    constructor() {
    }

    set_dataCode(dataCode : Buffer){
        this.dataCode = dataCode;
    }

    setAppUnit(appUnit : AxdrOctetString){
        this.resetChoices();
        this.appUnit = appUnit;
        this.choice = AppUnitChoice.APP_UNIT;
    }

    setAppUnitPalin(appUnitPalin : AxdrOctetString){
        this.resetChoices();
        this.appUnitPalin = appUnitPalin;
        this.choice = AppUnitChoice.APP_UNIT_PALIN;
    }

    decode(input: ReverseByteArrayInputStream): number {
        let codeLength = 0;
        let choosen = new AxdrEnum();
        choosen.set_const();

        codeLength += choosen.decode(input);
        this.resetChoices();
        this.choice = choosen.getValue();

        if (this.choice == AppUnitChoice.APP_UNIT_PALIN) {
            this.appUnitPalin = new AxdrOctetString();
            codeLength += this.appUnitPalin.decode(input);
            return codeLength;
        }else if (this.choice == AppUnitChoice.APP_UNIT) {
            this.appUnit = new AxdrOctetString();
            codeLength += this.appUnit.decode(input);
            return codeLength;
        }
        throw new Error("Error decoding AxdrChoice: Identifier matched to no item.");
    }

    encode(output: ReverseByteArrayOutputStream): number {
        if (this.dataCode != null) {
            for (let i = this.dataCode.length - 1; i >= 0; i--) {
                output.write(this.dataCode[i]);
            }
            return this.dataCode.length;

        }
        if (this.choice == AppUnitChoice._ERR_NONE_SELECTED) {
            throw new Error("Error encoding AxdrChoice: No item in choice was selected.");
        }

        let codeLength = 0;

        if (this.choice == AppUnitChoice.APP_UNIT) {
            if(this.appUnit != null){
                codeLength += this.appUnit.encode(output);
                let c = new AxdrEnum();
                c.set_min_max_val(1)
                codeLength += c.encode(output);
                return codeLength;
            }else{
                throw new Error("appunit is null");
            }
        }else if (this.choice == AppUnitChoice.APP_UNIT_PALIN) {
            if(this.appUnitPalin != null){
                codeLength += this.appUnitPalin.encode(output);
                let c = new AxdrEnum();
                c.set_min_max_val(0);
                codeLength += c.encode(output);
                return codeLength;
            }else{
                throw new Error("appunitpalin is null");
            }
        }
        throw new Error("Error encoding AxdrChoice: No item in choice was encoded.");
    }

    encodeAndSave(encodingSizeGuess: number) {
        let revOStream = new ReverseByteArrayOutputStream();
        revOStream.setBufSize(encodingSizeGuess);
        this.encode(revOStream);
        this.dataCode = revOStream.getArray();
    }

    getChoiceIndex(): AppUnitChoice {
        return this.choice;
    }

    resetChoices() {
        this.choice = AppUnitChoice._ERR_NONE_SELECTED;
        this.appUnitPalin = null;
        this.appUnit = null;
    }

    toString(): string {
        if (this.choice == AppUnitChoice.APP_UNIT_PALIN) {
            if (this.appUnitPalin != null) {
                return "choice: {appUnitPalin: " + this.appUnitPalin + "}";
            } else {
                return "choice is appUnitPalin but appUnitPalin is null";
            }
        }else if(this.choice == AppUnitChoice.APP_UNIT){
            if(this.appUnit != null){
                return "choice: {appUnit: " + this.appUnit + "}";
            }else{
                return "choice is appUnit but appUnit is null";
            }
        }else {
            return "unknown";
        }
    }

}

enum DataValidChoice {
    _ERR_NONE_SELECTED = -1,
    SID_MAC = 0,
    RN = 1,
    RN_MAC = 2,
    SID = 3,
}

export class SubChoiceDataValid implements AxdrType{

    dataCode : Buffer | null = null;
    choice : DataValidChoice | null = null;
    sid : SID | null = null;
    sidMac : SID_MAC | null = null;
    rn : RN | null = null;
    rnMac : RN_MAC | null = null;

    constructor() {
    }

    set_dataCode(dataCode : Buffer){
        this.dataCode = dataCode;
    }

    setRn(rn : RN){
        this.resetChoices();
        this.choice = DataValidChoice.RN;
        this.rn = rn;
    }

    setRnMac(rnMac : RN_MAC){
        this.resetChoices();
        this.choice = DataValidChoice.RN_MAC;
        this.rnMac = rnMac;
    }

    setSID(sid : SID){
        this.resetChoices();
        this.choice = DataValidChoice.SID;
        this.sid = sid;
    }

    setSidMac(sidMac : SID_MAC){
        this.resetChoices();
        this.choice = DataValidChoice.SID_MAC;
        this.sidMac = sidMac;
    }

    decode(input: ReverseByteArrayInputStream): number {
        let codeLength = 0;
        let choosen = new AxdrEnum();
        choosen.set_const();

        codeLength += choosen.decode(input);
        this.resetChoices();
        this.choice = choosen.getValue();

        if (this.choice == DataValidChoice.SID_MAC) {
            this.sidMac = new SID_MAC();
            codeLength += this.sidMac.decode(input);
            return codeLength;
        }else if (this.choice == DataValidChoice.RN) {
            this.rn = new RN();
            codeLength += this.rn.decode(input);
            return codeLength;
        }else if (this.choice == DataValidChoice.RN_MAC) {
            this.rnMac = new RN_MAC();
            codeLength += this.rnMac.decode(input);
            return codeLength;
        }else if (this.choice == DataValidChoice.SID) {
            this.sid = new SID();
            codeLength += this.sid.decode(input);
            return codeLength;
        }
        throw new Error("Error decoding AxdrChoice: Identifier matched to no item.");
    }

    encode(output: ReverseByteArrayOutputStream): number {

        if (this.dataCode != null) {
            for (let i = this.dataCode.length - 1; i >= 0; i--) {
                output.write(this.dataCode[i]);
            }
            return this.dataCode.length;
        }
        if (this.choice == DataValidChoice._ERR_NONE_SELECTED) {
            throw new Error("Error encoding AxdrChoice: No item in choice was selected.");
        }

        let codeLength = 0;

        if (this.choice == DataValidChoice.SID) {
            codeLength += this.sid.encode(output);
            let c = new AxdrEnum();
            c.set_min_max_val(3);
            codeLength += c.encode(output);
            return codeLength;
        }else if (this.choice == DataValidChoice.RN_MAC) {
            codeLength += this.rnMac.encode(output);
            let c = new AxdrEnum();
            c.set_min_max_val(2);
            codeLength += c.encode(output);
            return codeLength;
        }else if (this.choice == DataValidChoice.RN) {
            codeLength += this.rn.encode(output);
            let c = new AxdrEnum();
            c.set_min_max_val(1)
            codeLength += c.encode(output);
            return codeLength;
        }else if (this.choice == DataValidChoice.SID_MAC) {
            codeLength += this.sidMac.encode(output);
            let c = new AxdrEnum();
            c.set_min_max_val(0);
            codeLength += c.encode(output);
            return codeLength;
        }
        throw new Error("Error encoding AxdrChoice: No item in choice was encoded.");
    }

    encodeAndSave(encodingSizeGuess: number) {
        let revOStream = new ReverseByteArrayOutputStream();
        revOStream.setBufSize(encodingSizeGuess);
        this.encode(revOStream);
        this.dataCode = revOStream.getArray();
    }

    getChoiceIndex(): DataValidChoice {
        return this.choice;
    }

    resetChoices() {
        this.choice = DataValidChoice._ERR_NONE_SELECTED;
        this.sid = null;
        this.sidMac = null;
        this.rn = null;
        this.rnMac = null;
    }

    toString(): string {
        if (this.choice == DataValidChoice.SID_MAC) {
            if (this.sidMac != null) {
                return "choice: {sidMac: " + this.sidMac + "}";
            } else {
                return "choice is sidMac but sidMac is null";
            }
        }else if(this.choice == DataValidChoice.SID){
            if(this.sid != null){
                return "choice: {sid: " + this.sid + "}";
            }else{
                return "choice is sid but sid is null";
            }
        }else if(this.choice == DataValidChoice.RN_MAC){
            if(this.rnMac != null){
                return "choice: {rnMac: " + this.rnMac + "}";
            }else{
                return "choice is rnMac but rnMac is null";
            }
        }else if(this.choice == DataValidChoice.RN){
            if(this.rn != null){
                return "choice: {rn: " + this.rn + "}";
            }else{
                return "choice is rn but rn is null";
            }
        }else {
            return "unknown";
        }
    }

}


export class Security_Request implements AxdrType{

    dataCode : Buffer | null = null;
    appUnit : SubChoiceAppUnit | null = null;
    dataValid : SubChoiceDataValid | null = null;

    constructor() {
    }

    set_dataCode(dataCode : Buffer){
        this.dataCode = dataCode;
    }

    set_all(appUnit : SubChoiceAppUnit, dataValid : SubChoiceDataValid){
        this.appUnit = appUnit;
        this.dataValid = dataValid;
    }

    setAppUnit(appUnit : SubChoiceAppUnit){
        this.appUnit = appUnit;
    }

    setDataValid(dataValid : SubChoiceDataValid){
        this.dataValid = dataValid;
    }

    decode(input: ReverseByteArrayInputStream): number {
        let codeLength = 0;

        this.appUnit = new SubChoiceAppUnit();
        codeLength += this.appUnit.decode(input);

        this.dataValid = new SubChoiceDataValid();
        codeLength += this.dataValid.decode(input);

        return codeLength;
    }

    encode(output: ReverseByteArrayOutputStream): number {
        let codeLength;

        if (this.dataCode != null) {
            codeLength = this.dataCode.length;
            for (let i = this.dataCode.length - 1; i >= 0; i--) {
                output.write(this.dataCode[i]);
            }
        } else {
            if(this.dataValid != null && this.appUnit != null){
                codeLength = 0;
                codeLength += this.dataValid.encode(output);
                codeLength += this.appUnit.encode(output);
            }else {
                throw new Error("dataValid or appUnit is null");
            }

        }
        return codeLength;
    }

    encodeAndSave(encodingSizeGuess: number) {
        let revOStream = new ReverseByteArrayOutputStream();
        revOStream.setBufSize(encodingSizeGuess);
        this.encode(revOStream);
        this.dataCode = revOStream.getArray();
    }

    toString(): string {
        if (this.appUnit != null && this.dataValid != null) {
            return "sequence: {"+ "appUnit: " + this.appUnit + ", dataValid: " + this.dataValid  + "}";
        } else {
            return "appUnit or dataValid is null";
        }
    }

}
